package com._21cn.framework.utils.crypt;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.binary.StringUtils;

public class HmacUtil {
    private static final int STREAM_BUFFER_LENGTH = 1024;

    private static final String HmacMD5 = "HmacMD5";
    private static final String HmacSHA1 = "HmacSHA1";
    private static final String HmacSHA256 = "HmacSHA256";
    private static final String HmacSHA384 = "HmacSHA384";
    private static final String HmacSHA512 = "HmacSHA512";
	
    public static Mac getMac(final String algorithm) {
        try {
            return Mac.getInstance(algorithm);
        } catch (final NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(e);
        }
    }
    
    public static Mac getMac( final String algorithm, SecretKey key ){
    	Mac mac = getMac( algorithm );
    	try {
	        mac.init( key  );
        }
        catch( InvalidKeyException e ) {
        	throw new IllegalArgumentException(e);
        }
    	return mac;
    }

    public static Mac getMac( final String algorithm, byte[] key ){
    	return getMac( algorithm, new SecretKeySpec( key, algorithm ) );
    }


    public static Mac getMac( final String algorithm, String key ){
    	try {
	        return getMac( algorithm, Hex.decodeHex( key.toCharArray() ) );
        }
        catch( DecoderException e ) {
        	throw new IllegalArgumentException(e);
        }
    }

    public static Mac getHmacMD5(){
    	return getMac( HmacMD5 );
    }

    public static Mac getHmacSHA1(){
    	return getMac( HmacSHA1 );
    }

    public static Mac getHmacSHA256(){
    	return getMac( HmacSHA256 );
    }

    public static Mac getHmacSHA384(){
    	return getMac( HmacSHA384 );
    }

    public static Mac getHmacSHA512(){
    	return getMac( HmacSHA512 );
    }
    
    public static Mac getHmacMD5( byte[] key ){
    	return getMac( HmacMD5, key );
    }

    public static Mac getHmacMD5( SecretKey key ){
    	return getMac( HmacMD5, key );
    }

    public static Mac getHmacMD5( String key ){
    	return getMac( HmacMD5, key );
    }


    public static Mac getHmacSHA1( byte[] key ){
    	return getMac( HmacSHA1 );
    }

    public static Mac getHmacSHA1( SecretKey key ){
    	return getMac( HmacSHA1, key );
    }

    public static Mac getHmacSHA1( String key ){
    	return getMac( HmacSHA1, key );
    }

    public static Mac getHmacSHA256( byte[] key ){
    	return getMac( HmacSHA256 );
    }

    public static Mac getHmacSHA256( SecretKey key ){
    	return getMac( HmacSHA256, key );
    }

    public static Mac getHmacSHA256( String key ){
    	return getMac( HmacSHA256, key );
    }

    public static Mac getHmacSHA384( byte[] key ){
    	return getMac( HmacSHA384 );
    }

    public static Mac getHmacSHA384( SecretKey key ){
    	return getMac( HmacSHA384, key );
    }

    public static Mac getHmacSHA384( String key ){
    	return getMac( HmacSHA384, key );
    }

    public static Mac getHmacSHA512( byte[] key ){
    	return getMac( HmacSHA512 );
    }
    
    public static Mac getHmacSHA512( SecretKey key ){
    	return getMac( HmacSHA512, key );
    }

    public static Mac getHmacSHA512( String key ){
    	return getMac( HmacSHA512, key );
    }

    
    public static Mac updateDigest(final Mac mac, final InputStream data) throws IOException {
        final byte[] buffer = new byte[STREAM_BUFFER_LENGTH];
        int read = data.read(buffer, 0, STREAM_BUFFER_LENGTH);

        while (read > -1) {
        	mac.update(buffer, 0, read);
            read = data.read(buffer, 0, STREAM_BUFFER_LENGTH);
        }

        return mac;
    }
    
    public static Mac updateDigest(final Mac mac, final String valueToDigest) {
    	mac.update(StringUtils.getBytesUtf8(valueToDigest));
        return mac;
    }
    
    public static Mac updateDigest(final Mac mac, final byte[] valueToDigest) {
    	mac.update(valueToDigest);
        return mac;
    }
    
    public static byte[] hmacMD5(final String data, byte[] key ) {
    	return updateDigest( getHmacMD5( key ), data).doFinal();
    }
    public static byte[] hmacMD5(final byte[] data, final byte[] key ) {
        return updateDigest( getHmacMD5( key ), data).doFinal();
    }
    public static byte[] hmacMD5(final InputStream data, final byte[] key ) throws IOException {
        return updateDigest( getHmacMD5( key ), data).doFinal();
    }
    public static String hmacMD5Hex(final byte[] data, final byte[] key) {
        return Hex.encodeHexString(hmacMD5( data, key ) );
    }
    public static String hmacMD5Hex(final InputStream data, final byte[] key) throws IOException {
        return Hex.encodeHexString(hmacMD5( data, key ) );
    }
    public static String hmacMD5Hex(final String data, final byte[] key) {
        return Hex.encodeHexString(hmacMD5( data, key ) );
    }
    
    public static byte[] hmacMD5(final String data, final String key ) {
    	return updateDigest( getHmacMD5( key ), data).doFinal();
    }
    public static byte[] hmacMD5(final byte[] data, final String key ) {
        return updateDigest( getHmacMD5( key ), data).doFinal();
    }
    public static byte[] hmacMD5(final InputStream data, final String key ) throws IOException {
        return updateDigest( getHmacMD5( key ), data).doFinal();
    }
    public static String hmacMD5Hex(final byte[] data, final String key) {
        return Hex.encodeHexString(hmacMD5( data, key ) );
    }
    public static String hmacMD5Hex(final InputStream data, final String key) throws IOException {
        return Hex.encodeHexString(hmacMD5( data, key ) );
    }
    public static String hmacMD5Hex(final String data, final String key) {
        return Hex.encodeHexString(hmacMD5( data, key ) );
    }
    
    public static byte[] hmacSHA1(final String data, byte[] key ) {
    	return updateDigest( getHmacSHA1( key ), data).doFinal();
    }
    public static byte[] hmacSHA1(final byte[] data, final byte[] key ) {
        return updateDigest( getHmacSHA1( key ), data).doFinal();
    }
    public static byte[] hmacSHA1(final InputStream data, final byte[] key ) throws IOException {
        return updateDigest( getHmacSHA1( key ), data).doFinal();
    }
    public static String hmacSHA1Hex(final byte[] data, final byte[] key) {
        return Hex.encodeHexString(hmacSHA1( data, key ) );
    }
    public static String hmacSHA1Hex(final InputStream data, final byte[] key) throws IOException {
        return Hex.encodeHexString(hmacSHA1( data, key ) );
    }
    public static String hmacSHA1Hex(final String data, final byte[] key) {
        return Hex.encodeHexString(hmacSHA1( data, key ) );
    }
    public static byte[] hmacSHA1(final String data, final String key ) {
    	return updateDigest( getHmacSHA1( key ), data).doFinal();
    }
    public static byte[] hmacSHA1(final byte[] data, final String key ) {
        return updateDigest( getHmacSHA1( key ), data).doFinal();
    }
    public static byte[] hmacSHA1(final InputStream data, final String key ) throws IOException {
        return updateDigest( getHmacSHA1( key ), data).doFinal();
    }
    public static String hmacSHA1Hex(final byte[] data, final String key) {
        return Hex.encodeHexString(hmacSHA1( data, key ) );
    }
    public static String hmacSHA1Hex(final InputStream data, final String key) throws IOException {
        return Hex.encodeHexString(hmacSHA1( data, key ) );
    }
    public static String hmacSHA1Hex(final String data, final String key) {
        return Hex.encodeHexString(hmacSHA1( data, key ) );
    }

    public static byte[] hmacSHA256(final String data, byte[] key ) {
    	return updateDigest( getHmacSHA256( key ), data).doFinal();
    }
    public static byte[] hmacSHA256(final byte[] data, final byte[] key ) {
        return updateDigest( getHmacSHA256( key ), data).doFinal();
    }
    public static byte[] hmacSHA256(final InputStream data, final byte[] key ) throws IOException {
        return updateDigest( getHmacSHA256( key ), data).doFinal();
    }
    public static String hmacSHA256Hex(final byte[] data, final byte[] key) {
        return Hex.encodeHexString(hmacSHA256( data, key ) );
    }
    public static String hmacSHA256Hex(final InputStream data, final byte[] key) throws IOException {
        return Hex.encodeHexString(hmacSHA256( data, key ) );
    }
    public static String hmacSHA256Hex(final String data, final byte[] key) {
        return Hex.encodeHexString(hmacSHA256( data, key ) );
    }
    public static byte[] hmacSHA256(final String data, final String key ) {
    	return updateDigest( getHmacSHA256( key ), data).doFinal();
    }
    public static byte[] hmacSHA256(final byte[] data, final String key ) {
        return updateDigest( getHmacSHA256( key ), data).doFinal();
    }
    public static byte[] hmacSHA256(final InputStream data, final String key ) throws IOException {
        return updateDigest( getHmacSHA256( key ), data).doFinal();
    }
    public static String hmacSHA256Hex(final byte[] data, final String key) {
        return Hex.encodeHexString(hmacSHA256( data, key ) );
    }
    public static String hmacSHA256Hex(final InputStream data, final String key) throws IOException {
        return Hex.encodeHexString(hmacSHA256( data, key ) );
    }
    public static String hmacSHA256Hex(final String data, final String key) {
        return Hex.encodeHexString(hmacSHA256( data, key ) );
    }
    
    public static byte[] hmacSHA384(final String data, byte[] key ) {
    	return updateDigest( getHmacSHA384( key ), data).doFinal();
    }
    public static byte[] hmacSHA384(final byte[] data, final byte[] key ) {
        return updateDigest( getHmacSHA384( key ), data).doFinal();
    }
    public static byte[] hmacSHA384(final InputStream data, final byte[] key ) throws IOException {
        return updateDigest( getHmacSHA384( key ), data).doFinal();
    }
    public static String hmacSHA384Hex(final byte[] data, final byte[] key) {
        return Hex.encodeHexString(hmacSHA384( data, key ) );
    }
    public static String hmacSHA384Hex(final InputStream data, final byte[] key) throws IOException {
        return Hex.encodeHexString(hmacSHA384( data, key ) );
    }
    public static String hmacSHA384Hex(final String data, final byte[] key) {
        return Hex.encodeHexString(hmacSHA384( data, key ) );
    }
    public static byte[] hmacSHA384(final String data, final String key ) {
    	return updateDigest( getHmacSHA384( key ), data).doFinal();
    }
    public static byte[] hmacSHA384(final byte[] data, final String key ) {
        return updateDigest( getHmacSHA384( key ), data).doFinal();
    }
    public static byte[] hmacSHA384(final InputStream data, final String key ) throws IOException {
        return updateDigest( getHmacSHA384( key ), data).doFinal();
    }
    public static String hmacSHA384Hex(final byte[] data, final String key) {
        return Hex.encodeHexString(hmacSHA384( data, key ) );
    }
    public static String hmacSHA384Hex(final InputStream data, final String key) throws IOException {
        return Hex.encodeHexString(hmacSHA384( data, key ) );
    }
    public static String hmacSHA384Hex(final String data, final String key) {
        return Hex.encodeHexString(hmacSHA384( data, key ) );
    }
    
    public static byte[] hmacSHA512(final String data, byte[] key ) {
    	return updateDigest( getHmacSHA512( key ), data).doFinal();
    }
    public static byte[] hmacSHA512(final byte[] data, final byte[] key ) {
        return updateDigest( getHmacSHA512( key ), data).doFinal();
    }
    public static byte[] hmacSHA512(final InputStream data, final byte[] key ) throws IOException {
        return updateDigest( getHmacSHA512( key ), data).doFinal();
    }
    public static String hmacSHA512Hex(final byte[] data, final byte[] key) {
        return Hex.encodeHexString(hmacSHA512( data, key ) );
    }
    public static String hmacSHA512Hex(final InputStream data, final byte[] key) throws IOException {
        return Hex.encodeHexString(hmacSHA512( data, key ) );
    }
    public static String hmacSHA512Hex(final String data, final byte[] key) {
        return Hex.encodeHexString(hmacSHA512( data, key ) );
    }
    public static byte[] hmacSHA512(final String data, final String key ) {
    	return updateDigest( getHmacSHA512( key ), data).doFinal();
    }
    public static byte[] hmacSHA512(final byte[] data, final String key ) {
        return updateDigest( getHmacSHA512( key ), data).doFinal();
    }
    public static byte[] hmacSHA512(final InputStream data, final String key ) throws IOException {
        return updateDigest( getHmacSHA512( key ), data).doFinal();
    }
    public static String hmacSHA512Hex(final byte[] data, final String key) {
        return Hex.encodeHexString(hmacSHA512( data, key ) );
    }
    public static String hmacSHA512Hex(final InputStream data, final String key) throws IOException {
        return Hex.encodeHexString(hmacSHA512( data, key ) );
    }
    public static String hmacSHA512Hex(final String data, final String key) {
        return Hex.encodeHexString(hmacSHA512( data, key ) );
    }
    
    public static SecretKey newSecretKey( String algorithm )  {  
        try {
        	KeyGenerator generator = KeyGenerator.getInstance( algorithm );
        	return generator.generateKey();  
        }
        catch( NoSuchAlgorithmException e ) {
        	throw new IllegalArgumentException(e);
        }  
    } 
    
    public static byte[] newSecretKeyBytes( String algorithm )  { 
    	return newSecretKey( algorithm ).getEncoded();
    }  
  
    public static String newSecretKeyHex( String algorithm )  { 
    	return Hex.encodeHexString( newSecretKeyBytes( algorithm ) );
    }  

    public static String newHmacMD5KeyHex()  {  
    	return newSecretKeyHex( HmacMD5 );
    }  
  
    public static byte[] newHmacMD5Key()  {  
    	return newSecretKeyBytes( HmacMD5 );
    }  
  
    public static String newHmacSHA1KeyHex()  {  
    	return newSecretKeyHex( HmacSHA1 );
    }  
  
    public static byte[] newHmacSHA1Key()  {  
    	return newSecretKeyBytes( HmacSHA1 );
    }  
  
    public static String newHmacSHA384KeyHex()  {  
    	return newSecretKeyHex( HmacSHA384 );
    }  
  
    public static byte[] newHmacSHA384Key()  {  
    	return newSecretKeyBytes( HmacSHA384 );
    }  
  
    public static String newHmacSHA256KeyHex()  {  
    	return newSecretKeyHex( HmacSHA256 );
    }  
  
    public static byte[] newHmacSHA256Key()  {  
    	return newSecretKeyBytes( HmacSHA256 );
    }  
  
    public static String newHmacSHA512KeyHex()  {  
    	return newSecretKeyHex( HmacSHA512 );
    }  
  
    public static byte[] newHmacSHA512Key()  {  
    	return newSecretKeyBytes( HmacSHA512 );
    }  
 
}
